package cn.qingyun.gis.modules.express.util;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ReUtil;
import cn.qingyun.gis.modules.express.entity.Point;
import com.google.common.collect.Lists;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Polygon;

import java.math.RoundingMode;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern; 

public class GeoTool {

    public static Point getCenterPoint(String wkt) {
        try {
            LinkedList<Point> pointList = getPoints(wkt);
            int total = pointList.size();
            double X = 0, Y = 0, Z = 0;

            while (!pointList.isEmpty()) {
                Point point = pointList.pollFirst();
                if (point != null) {
                    double lat, lon, x, y, z;
                    lat = point.getLat().doubleValue() * Math.PI / 180;
                    lon = point.getLon().doubleValue() * Math.PI / 180;
                    x = Math.cos(lat) * Math.cos(lon);
                    y = Math.cos(lat) * Math.sin(lon);
                    z = Math.sin(lat);
                    X += x;
                    Y += y;
                    Z += z;
                }
            }


            X = X / total;
            Y = Y / total;
            Z = Z / total;
            double Lon = Math.atan2(Y, X);
            double Hyp = Math.sqrt(X * X + Y * Y);
            double Lat = Math.atan2(Z, Hyp);
            double longitude = Lon * 180 / Math.PI;
            double latitude = Lat * 180 / Math.PI;

            return Point.builder()
                    .lon(NumberUtil.toBigDecimal(longitude).setScale(6, RoundingMode.UP))
                    .lat(NumberUtil.toBigDecimal(latitude).setScale(6, RoundingMode.UP))
                    .build();
        } catch (Exception e) {
            return null;
        }
    }

    public static LinkedList<Point> getPoints(String wkt) {
        List<String> points = ReUtil.findAll(Pattern.compile("\\d{1,3}\\.\\d{3,} \\d{1,3}\\.\\d{3,}", Pattern.MULTILINE), wkt, 0);

        LinkedList<Point> result = Lists.newLinkedList();
        for (String point : points) {
            String[] split = point.split(" ");
            result.add(Point.builder()
                    .lon(NumberUtil.toBigDecimal(split[0]))
                    .lat(NumberUtil.toBigDecimal(split[1]))
                    .build());
        }
        return result;
    }

    /**
     * 将字符串多坐标点转换为坐标数组
     * @param latLonString "116.616634,40.272665 116.644733,40.280371 116.636181,40.264352 116.616634,40.272665"
     * @return org.locationtech.jts.geom.Coordinate[]
     */
    public static Coordinate[] getCoordinateArray(String latLonString){
        List<String> points = ReUtil.findAll(Pattern.compile("\\d{1,3}\\.\\d{3,} \\d{1,3}\\.\\d{3,}", Pattern.MULTILINE), latLonString, 0);


        //String[] split = latLonString.split(" ");
        Coordinate[] coordinateArray = new Coordinate[points.size() + 1];
        for (int i = 0; i < points.size(); i++) {
            String[] point = points.get(i).split(" ");
            Coordinate coordinate = new Coordinate(Double.valueOf(point[0]),Double.valueOf(point[1]));
            coordinateArray[i]=coordinate;
        }
        //保证首位地址相同
        String[] point = points.get(0).split(" ");
        Coordinate coordinateEnd = new Coordinate(Double.valueOf(point[0]),Double.valueOf(point[1]));
        coordinateArray[points.size()] = coordinateEnd;
        return coordinateArray;
    }


    /**
     * 判断坐标点是否在多坐标点组成的多边形面内
     * @param coordinateArray
     * @param coordinate
     * @return boolean
     * within 判断是否在内部，边缘点返回false
     * intersects 判断是否相交，弥补 within边缘点缺陷，
     */
    public static boolean withinAndIntersects(Coordinate[] coordinateArray,Coordinate coordinate){

        boolean result=false;
        //小于3个点无法组成多边形
        if (coordinateArray.length<3){
            return result;
        }

        //必须首尾坐标点相同组成闭合多边形
        if (!(coordinateArray[0].equals2D(coordinateArray[coordinateArray.length-1]))){
            return result;
        }

        GeometryFactory geometryFactory = new GeometryFactory();
        org.locationtech.jts.geom.Point point = geometryFactory.createPoint(coordinate);
        Polygon polygon = geometryFactory.createPolygon(coordinateArray);

        if (point.within(polygon)||point.intersects(polygon)){
            result=true;
        }
        return result;
    }


}
